package cn.turboinfo.fuyang.api.scheduler.listener.credit

import cn.turboinfo.fuyang.api.domain.common.service.company.HousekeepingCompanyService
import cn.turboinfo.fuyang.api.domain.common.service.credit.CreditRatingService
import cn.turboinfo.fuyang.api.domain.common.service.order.ServiceOrderService
import cn.turboinfo.fuyang.api.domain.common.service.product.ProductService
import cn.turboinfo.fuyang.api.domain.common.service.shop.HousekeepingShopService
import cn.turboinfo.fuyang.api.domain.common.service.staff.HousekeepingStaffService
import cn.turboinfo.fuyang.api.entity.common.constant.EventConstants
import cn.turboinfo.fuyang.api.entity.common.enumeration.common.EntityObjectType
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.ServiceOrderCreditRatingStatus
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.ServiceOrderStatus
import cn.turboinfo.fuyang.api.entity.common.event.credit.NewCreditRatingEvent
import cn.turboinfo.fuyang.api.provider.rabbit.constant.RabbitConstants
import mu.KotlinLogging
import org.springframework.amqp.core.Binding
import org.springframework.amqp.core.BindingBuilder
import org.springframework.amqp.core.Queue
import org.springframework.amqp.core.TopicExchange
import org.springframework.amqp.rabbit.annotation.RabbitHandler
import org.springframework.amqp.rabbit.annotation.RabbitListener
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.stereotype.Component
import java.math.BigDecimal
import java.math.RoundingMode

@Configuration
class HousekeepingCreditRatingRecalculateListenerConfig {

    companion object {
        private const val Name = "HousekeepingCreditRatingRecalculate"

        private const val Topic = EventConstants.TopicNewCreditRatingEvent

        const val QueueName =
            "${RabbitConstants.QueueEventPrefix}${Topic}.${Name}"

        const val RoutingKey = "${RabbitConstants.RoutingKeyEventPrefix}${Topic}"
    }

    @Bean
    fun housekeepingCreditRatingRecalculateQueue(): Queue {
        return Queue(QueueName)
    }

    @Bean
    protected fun housekeepingCreditRatingRecalculateBinding(
        housekeepingCreditRatingRecalculateQueue: Queue,
        eventExchange: TopicExchange
    ): Binding {
        return BindingBuilder.bind(housekeepingCreditRatingRecalculateQueue)
            .to(eventExchange)
            .with(RoutingKey)
    }

    @Component
    @RabbitListener(queues = [QueueName])
    class HousekeepingCreditRatingRecalculateListener(
        private val creditRatingService: CreditRatingService,
        private val serviceOrderService: ServiceOrderService,
        private val productService: ProductService,
        private val housekeepingStaffService: HousekeepingStaffService,
        private val housekeepingShopService: HousekeepingShopService,
        private val housekeepingCompanyService: HousekeepingCompanyService,
    ) {

        private val logger = KotlinLogging.logger {}

        @RabbitHandler
        fun process(event: NewCreditRatingEvent) {
            runCatching {
                event
                    .let {
                        creditRatingService.getByIdEnsure(event.creditRatingId)
                    }
                    ?.takeIf {
                        // 只处理服务订单的信用评价
                        it.objectType == EntityObjectType.SERVICE_ORDER
                    }
                    ?.also { creditRating ->
                        // 有新的服务订单评价, 需要重新计算涉及的服务人员和企业的评分

                        // 拿到服务订单详情
                        val serviceOrder = serviceOrderService.getByIdEnsure(creditRating.objectId)

                        // 服务产品ID
                        val productId = serviceOrder.productId
                        calculateProductCreditRating(productId)

                        // 对订单进行服务的员工ID
                        val staffId = serviceOrder.staffId
                        calculateStaffCreditRating(staffId)

                        // 所属门店ID
                        val shopId = serviceOrder.shopId
                        calculateShopCreditRating(shopId)

                        // 所属公司ID
                        val companyId = serviceOrder.companyId
                        calculateCompanyCreditRating(companyId)
                    }
            }
                .onFailure {
                    logger.error(it) { "处理信用评价重算事件消息出错" }
                }
        }

        private fun calculateProductCreditRating(productId: Long) {
            // 取该产品的所有历史服务过的订单计算服务人员的评价分
            // TODO 暂时简单粗暴全拿出来使用
            val orderList = serviceOrderService.findByProductId(productId, ServiceOrderStatus.COMPLETED)
                .filter {
                    // 只计算已评价的订单
                    it.creditRatingStatus == ServiceOrderCreditRatingStatus.SUBMITTED
                }
            val creditRatingList =
                creditRatingService.findByObjectIdCollection(EntityObjectType.SERVICE_ORDER, orderList.map { it.id })

            // 计算产品的评价分
            val rating = creditRatingList
                .map {
                    // 五星评价换算成百分制
                    it.score * 20
                }
                .average()

            val creditScore = BigDecimal(rating).setScale(2, RoundingMode.HALF_UP)
            productService.updateCreditScore(productId, creditScore)
        }

        private fun calculateStaffCreditRating(staffId: Long) {
            // 取该服务人员的所有历史服务过的订单计算服务人员的评价分
            // TODO 暂时简单粗暴全拿出来使用
            val orderList = serviceOrderService.findByStaffId(staffId, ServiceOrderStatus.COMPLETED)
                .filter {
                    // 只计算已评价的订单
                    it.creditRatingStatus == ServiceOrderCreditRatingStatus.SUBMITTED
                }
            val creditRatingList =
                creditRatingService.findByObjectIdCollection(EntityObjectType.SERVICE_ORDER, orderList.map { it.id })

            // 计算服务人员的评价分
            val rating = creditRatingList
                .map {
                    // 五星评价换算成百分制
                    it.score * 20
                }
                .average()

            val creditScore = BigDecimal(rating).setScale(2, RoundingMode.HALF_UP)
            housekeepingStaffService.updateCreditScore(staffId, creditScore)
        }

        private fun calculateShopCreditRating(shopId: Long) {
            // 取该门店的所有历史服务过的订单计算服务人员的评价分
            // TODO 暂时简单粗暴全拿出来使用
            val orderList = serviceOrderService.findByShopId(shopId, ServiceOrderStatus.COMPLETED)
                .filter {
                    // 只计算已评价的订单
                    it.creditRatingStatus == ServiceOrderCreditRatingStatus.SUBMITTED
                }
            val creditRatingList =
                creditRatingService.findByObjectIdCollection(EntityObjectType.SERVICE_ORDER, orderList.map { it.id })

            // 计算门店的评价分
            val rating = creditRatingList
                .map {
                    // 五星评价换算成百分制
                    it.score * 20
                }
                .average()

            val creditScore = BigDecimal(rating).setScale(2, RoundingMode.HALF_UP)
            housekeepingShopService.updateCreditScore(shopId, creditScore)
        }

        private fun calculateCompanyCreditRating(companyId: Long) {
            // 取该公司的所有历史服务过的订单计算服务人员的评价分
            // TODO 暂时简单粗暴全拿出来使用
            val orderList = serviceOrderService.findByCompanyId(companyId, ServiceOrderStatus.COMPLETED)
                .filter {
                    // 只计算已评价的订单
                    it.creditRatingStatus == ServiceOrderCreditRatingStatus.SUBMITTED
                }
            val creditRatingList =
                creditRatingService.findByObjectIdCollection(EntityObjectType.SERVICE_ORDER, orderList.map { it.id })

            // 计算公司的评价分
            val rating = creditRatingList
                .map {
                    // 五星评价换算成百分制
                    it.score * 20
                }
                .average()

            val creditScore = BigDecimal(rating).setScale(2, RoundingMode.HALF_UP)
            housekeepingCompanyService.updateCreditScore(companyId, creditScore)
        }
    }
}